// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: xguo

#include "coin2/exchange/mexc_futures/api_order/native_private_client.h"

#include <list>
#include <memory>
#include <sstream>
#include <string>

#include "coin2/exchange/base/api_util/url_params_builder.h"
#include "coin2/exchange/mexc_futures/symbology/product.h"
// #include "coin2/exchange/mexc_futures_base/api_util/rate_tracker.h"

namespace coin2::exchange::mexc_futures::api_order {

using coin2::exchange::mexc_futures::symbology::MexcFuturesProduct;

#define CHECK_API_LIMIT(...)

MexcFuturesNativePrivateClient::MexcFuturesNativePrivateClient(
    const MarketExchangeApi& mea,
    const NativeClientConfig& config_override) {
  mea_ = mea;
  NativeClientConfig config;
  config.set_rest_host("contract.mexc.com");
  config.set_rest_host_port(443);
  config.MergeFrom(config_override);
  LOG(INFO) << "[MexcFuturesNativePrivateClient] config: " << config.DebugString();

  auths_.Construct<MexcRestAuth, NativeClientConfig>(config);
  // rate_tracker_ = std::make_unique<MexcFuturesRateTracker>(mea_, config);
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::QueryAccountBalance() {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;
  auto context = std::shared_ptr<AsioHttpContext>(
      GetAuth()->CreateRequestByGetWithSignature("api/v1/private/account/assets", &builder));

  Push(10, ts, ActionType::QUERY);
  context->query_type = OrderSubTopicId::REST_ACCOUNT_BALANCE;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::QueryAccountPosition() {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;
  auto context = std::shared_ptr<AsioHttpContext>(GetAuth()->CreateRequestByGetWithSignature(
      "api/v1/private/position/open_positions",
      &builder));

  Push(10, ts, ActionType::QUERY);
  context->query_type = OrderSubTopicId::REST_ACCOUNT_POSITION;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::QueryOpenOrders() {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;
  builder.PutGet("page_num", 1);
  builder.PutGet("page_size", 100);
  auto context = std::shared_ptr<AsioHttpContext>(GetAuth()->CreateRequestByGetWithSignature(
      "api/v1/private/order/list/open_orders/",
      &builder));

  CHECK_API_LIMIT(ActionType::QUERY);
  Push(3, ts, ActionType::QUERY);
  context->query_type = OrderSubTopicId::REST_OPEN_ORDERS;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::QueryAllOrders() {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;

  builder.PutGet("page_num", 1);
  builder.PutGet("page_size", 100);
  auto context = std::shared_ptr<AsioHttpContext>(GetAuth()->CreateRequestByGetWithSignature(
      "api/v1/private/order/list/history_orders",
      &builder));

  CHECK_API_LIMIT(ActionType::QUERY);
  Push(10, ts, ActionType::QUERY);
  context->query_type = OrderSubTopicId::REST_ALL_ORDERS;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::QueryTrades(
    const std::string& native_symbol) {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;

  builder.PutGet("symbol", native_symbol);
  builder.PutGet("page_num", 1);
  builder.PutGet("page_size", 100);
  auto context = std::shared_ptr<AsioHttpContext>(GetAuth()->CreateRequestByGetWithSignature(
      "api/v1/private/order/list/order_deals",
      &builder));

  CHECK_API_LIMIT(ActionType::QUERY);

  Push(10, ts, ActionType::QUERY);
  context->query_type = OrderSubTopicId::REST_FILL_ORDERS;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::PlaceOrder(
    const OrderSpec& order_spec,
    int64_t proc_order_id) {
  using OrderDuration = coin::proto::OrderDuration;

  UrlParamsBuilder builder;
  auto ts = GetCurrentTimestamp();

  MexcFuturesProduct product =
      MexcFuturesProduct::FromStr(order_spec.product, order_spec.order_created_time);
  auto product_holder = GetProductHolder(product, order_spec.order_created_time);
  auto formatter = product_holder->product_info().order_formatter();

  // required field: symbol, order_side, order_type, price, vol, external_oid
  builder.PutGet("symbol", order_spec.product);

  // 1 isolated
  // 2 crossed
  builder.PutGet("openType", "2");  // fixed to crossed mode.

  // 1 open long --> buy open
  // 2 close short --> buy close
  // 3 open short --> sell open
  // 4 close long --> sell close
  switch (order_spec.order_side) {
    case OrderSide::BUY_OPEN_ORDER: {
      builder.PutGet("side", "1");
      break;
    }
    case OrderSide::BUY_CLOSE_ORDER: {
      builder.PutGet("side", "2");
      break;
    }
    case OrderSide::SELL_OPEN_ORDER: {
      builder.PutGet("side", "3");
      break;
    }
    case OrderSide::SELL_CLOSE_ORDER: {
      builder.PutGet("side", "4");
      break;
    }
    default: {
      CHECK(false) << "Unknown side: " << static_cast<int>(order_spec.order_side);
    }
  }

  // 1 price limited order.
  // 2 post only maker.
  // 3 transact or cancel instantly.
  // 4 transact completely or cancel completely.
  // 5 market order.
  // 6 convert market price to current price.
  switch (order_spec.order_type) {
    case OrderType::LIMIT_ORDER: {
      builder.PutGet("type", "1");
      break;
    }
    case OrderType::IOC_LIMIT_ORDER: {
      CHECK_EQ(order_spec.duration, OrderDuration::IOC_ORDER);
      builder.PutGet("type", "3");
      break;
    }
    case OrderType::FOK_LIMIT_ORDER: {
      CHECK_EQ(order_spec.duration, OrderDuration::FOK_ORDER);
      builder.PutGet("type", "4");
      break;
    }
    default: {
      CHECK(false) << "Unsupported order type: " << static_cast<int>(order_spec.order_type);
    }
  }
  if (order_spec.post_only) {
    builder.PutGet("type", "2");
  }

  builder.PutGet("price", formatter.FormatPrice(order_spec.price));
  builder.PutGet("vol", formatter.FormatQty(order_spec.qty));
  builder.PutGet("externalOid", proc_order_id);
  // params.set_prosition_id();  // WHAT is position id ????

  auto context = std::shared_ptr<AsioHttpContext>(
      GetFirstAuth()->CreateRequestByPostWithSignature("api/v1/private/order/submit", &builder));
  CHECK_API_LIMIT(ActionType::PLACE);

  Push(1, ts, ActionType::PLACE);
  context->query_type = OrderSubTopicId::SUBMIT_RESPONSE;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::CancelOrder(
    const std::string& exchange_order_id) {
  auto ts = GetCurrentTimestamp();
  UrlParamsBuilder builder;
  auto context = std::shared_ptr<AsioHttpContext>(
      GetFirstAuth()->CreateRequestByDeleteWithSignature("api/v1/private/order/cancel", &builder));

  CHECK_API_LIMIT(ActionType::CANCEL);
  Push(1, ts, ActionType::CANCEL);
  context->query_type = OrderSubTopicId::CANCEL_RESPONSE;
  return context;
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::PlaceOrderNorm(
    const OrderExecution& order_exe) {
  const auto& order_spec = *order_exe.order_spec;
  const auto proc_order_id = order_exe.proc_order_id;
  return PlaceOrder(order_spec, proc_order_id);
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::CancelOrderNorm(
    const OrderExecution& order_exe) {
  const std::string exchange_order_id = order_exe.oc->exchange_order_id();
  return CancelOrder(exchange_order_id);
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::SubmitOrderExecution(
    const OrderExecutionProto& order) {
  switch (order.type()) {
    case OrderExecutionProto::SUBMIT_ORDER: {
      auto order_spec = OrderSpec::FromProto(order.order_spec());
      return PlaceOrder(*order_spec, order.proc_order_id());
    }
    case OrderExecutionProto::CANCEL_ORDER: {
      CHECK(false);
      return CancelOrder(0);
    }
    default: {
      return nullptr;
    }
  }
}

std::shared_ptr<AsioHttpContext> MexcFuturesNativePrivateClient::SubmitRestQuery(
    const RestQueryProto& query) {
  switch (query.type()) {
    case RestQueryProto::QUERY_ACCOUNT_BALANCE: {
      return QueryAccountBalance();
    }
    case RestQueryProto::QUERY_OPEN_ORDERS: {
      return QueryOpenOrders();
    }
    default: {
      return nullptr;
    }
  }
}

}  // namespace coin2::exchange::mexc_futures::api_order
